//CEPoll_IOListener.cpp
//
#include "StdAfx.h"

#ifdef LINUX

#include "EPoll_IOListener.h"
#include "SessionSync.h"
#include "NetService.h"

EPoll_IOListener::EPoll_IOListener(){
	epoll_fd_ = 0;
	events_ = 0;
	listen_session_ = 0;
}

EPoll_IOListener::~EPoll_IOListener(){
	stop();
	SAFE_DELETE( listen_session_ );
	SAFE_DELETE_ARRAY(events_);
}

bool EPoll_IOListener::initialize( unsigned int event_size, int port, int back_log){
	IS_NULL_RETURN( event_size, false );	
	IS_NULL_RETURN( port, false );	

	event_size_ = event_size + 1;
	events_ = new epoll_event[event_size];
	epoll_fd_ = epoll_create(event_size);

	if( epoll_fd_ < 0 )
		return false;

	listen_session_ = new SessionSync();

	if(! listen_session_->getPeerInstance()->listen( port, back_log ) ){		
		LOG_ERR( LOG_SERVICE( "instance" )->Log("%s - %u", __FUNCTION__, __LINE__) );
		return false;
	}

	if(! registerSession( listen_session_  ) )
		return false;

	return CThread::start(0, this);	
}

bool EPoll_IOListener::registerSession(NetworkSession* session){
	struct epoll_event event;
	event.events = EPOLLIN;	
    event.data.ptr = (void*)session;

    if( epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, session->getPeer(), &event) == -1 ){
		LOG_ERR( LOG_SERVICE( "instance" )->Log("%s - %u", __FUNCTION__, __LINE__) );
		return false;
	}
	
	LOG_DEBUG( LOG_SERVICE( "instance" )->Log("%s - %u", __FUNCTION__, __LINE__) );

	return true;
}

bool EPoll_IOListener::unregisterSession(NetworkSession* session){
	struct epoll_event event;
	event.events = EPOLLIN;	
    event.data.ptr = (void*)session;

	if( epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, session->getPeer(), &event) == -1 )		
		return false;
	return true;
}

int EPoll_IOListener::acceptSession(){
	char str_ip[ DEF_IP_LEN ];
	memset( str_ip, 0, DEF_IP_LEN );	

	SOCKET sock = listen_session_->getPeerInstance()->accept_socket( str_ip );

	if( sock == INVALID_SOCKET ){		
		LOG_ERR( LOG_SERVICE( "instance" )->Log("%s - %u - sock = %i", __FUNCTION__, __LINE__, \
			sock ) );

		return -1;
	}

	Socket socket;
	socket.setPeer( sock, str_ip );
	if(! NetService::Instance()->registerSession( socket ) ){
		LOG_ERR( LOG_SERVICE( "instance" )->Log("%s - %u - sock = %i", __FUNCTION__, __LINE__, \
			sock ) );

		socket.close_socket();

		return -1;
	}
	
	return 0;
}

DEF_THREAD_RETURN EPoll_IOListener::run(){
	LOG_INSTANCE( LOG_SERVICE( "instance" )->Log("EPoll thread start %s = %u" \
			, __FILE__, getThreadID()) );

	size_t event_count = 0;
	size_t i = 0;
	NetworkSession* session = 0;

	while( isActive() ){
		event_count = epoll_wait( epoll_fd_, events_, event_size_, DEF_WAIT_EVENT_TIME );

		if( event_count == 0 )
			continue;

		if( event_count == -1 ){
			LOG_ERR( LOG_SERVICE( "instance" )->Log("%s - %u", __FUNCTION__, __LINE__) );
			continue;
		}
		
		for( i = 0 ; i < event_count ; i++ ){
			session = (SessionSync*)events_[i].data.ptr;
			if(! session ){
				LOG_ERR( LOG_SERVICE( "instance" )->Log("%s - %u", __FUNCTION__, __LINE__) );
				continue;
			}

			if( session == listen_session_ ){
				acceptSession();
				continue;
			}
			
			if( session->isCloseReserve() || session->isClose() ){
				unregisterSession( session );
				continue;
			}			

			if( session->read_handler() < 0 ){
				unregisterSession( session );
				session->close_handler( 1, "CEPoll_IOListener" );			
			}
        }
	}	

	LOG_INSTANCE( LOG_SERVICE( "instance" )->Log("epoll thread end %s = %u" \
			, __FILE__, getThreadID()) );

	return 0;
}

bool EPoll_IOListener::availableThisListener(){	
	int version0 = SystemInfomation::get_linux_kernel_version( 0 );
	int version1 = SystemInfomation::get_linux_kernel_version( 1 );
	int version2 = SystemInfomation::get_linux_kernel_version( 2 );
	int version3 = SystemInfomation::get_linux_kernel_version( 3 );

	if( version0 < 2 )
		return false;

	if( version1 < 6 )
		return false;

	if( version2 < 4 )
		return false;

	if( (version2 == 4) && (version3 < 52) )
		return false;

	return true;
}

#endif
